#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>  // 引入 ArduinoJson 库
#include <BLEDEV.h>       // 引入蓝牙库

// 设置要创建的热点名与密码
const char *apssid; // 热点名称将被动态分配
const char *appassword = "12345678";
////////////////////// MQTT 配置
#define mqttServer "iot.lciot.net" // 定义MQTT服务器的IP地址
#define mqttPort 1883              // 定义MQTT服务器的端口号，默认端口为1883
#define user "admin"               // 定义MQTT用户名
#define pass "123456"              // 定义MQTT密码
String clientId;             // 客户端ID
String pubTopic = "v1/devices/me/rpc/response";  // 发布消息的主题
#define subTopic "v1/devices/me/rpc/request/+"   // 订阅消息的主题

WiFiClient espClient;            // 定义wifiClient实例
PubSubClient client(espClient);  // 定义PubSubClient的实例

// 广播数据和响应数据的缓冲区
uint8_t adv_data[31];      // 广播数据缓冲区
uint8_t adv_rsp_data[31];  // 扫描响应数据缓冲区
char *stateMessage;//平台控制蓝牙通知
char *resultMessage;//蓝牙控制蓝牙通知

// 定义服务和特征的UUID
#define SERVICE_UUID1 "ff33"                              // 服务UUID1
#define SERVICE_UUID2 "4fafc2011fb5459e8fccc5c9c331914b"  // 服务UUID2

#define CHARACTERISTIC_UUID1 "FF11"                              // 特征UUID1
#define CHARACTERISTIC_UUID2 "FF22"                              // 特征UUID2
#define CHARACTERISTIC_UUID3 "ff11c2011fb5459e8fccc5c9c33191ff"  // 特征UUID3
// 设备连接状态变量
bool deviceConnected = false;     // 当前设备连接状态
bool oldDeviceConnected = false;  // 上一个设备连接状态

// BLE特征指针
BLECharacteristic *pCharacteristic1;  // 特征指针1
BLECharacteristic *pCharacteristic2;  // 特征指针2
BLECharacteristic *pCharacteristic3;  // 特征指针3
BLEAdvertising *pAdvertising;         // 广播指针

// 一个简单的用于通知的变量,用于特征的值
uint32_t value = 0;
// 声明标志位
bool RelayState = false;  // 定义GPIO 引脚的状态变量
bool LedState = true;
uint8_t RelayPin = 28;
uint8_t LedPin = 23;

// 添加变量控制RSSI上传间隔
unsigned long lastRSSIPublishTime = 0;
const unsigned long RSSI_INTERVAL = 5000;  // 设置RSSI更新间隔（例如每60秒上传一次）

// 自定义服务器回调类，继承 BLEServerCallbacks
class MyServerCallbacks : public BLEServerCallbacks {
  void onConnect() {                 // 当设备连接时调用
    deviceConnected = true;          // 更新连接状态
    Serial.println("蓝牙设备已连接...");  // 打印连接信息
  }

  void onDisconnect() {                 // 当设备断开连接时调用
    deviceConnected = false;            // 更新连接状态
    Serial.println("蓝牙设备已断开...");  // 打印断开连接信息
  }

  void onwrite(uint16_t prf_id, uint16_t att_idx, uint8_t *data, uint16_t data_len)
  {
    // 打印出写入操作的 profile ID、attribute index 和数据长度
    Serial.printf("prf_id:%d, att_idx:%d, data_len:%d\r\n", prf_id, att_idx, data_len);

    // 动态分配内存以存储接收到的字符串数据
    char *receivedData = (char *)malloc(data_len + 1);
    if (receivedData == NULL) {
      Serial.println("内存分配失败!");
      return;
    }

    // 将数据拷贝到字符串数组中
    memcpy(receivedData, data, data_len);
    receivedData[data_len] = '\0';  // 添加字符串结束符

    // 打印接收到的字符串
    Serial.printf("收到的数据: %s\r\n", receivedData);

    // 使用 ArduinoJson 库解析JSON数据
    StaticJsonDocument<200> doc;
    DeserializationError error = deserializeJson(doc, receivedData);

    if (error) {
      Serial.print("JSON 解析失败: ");
      Serial.println(error.c_str());
      free(receivedData);  // 解析失败后，释放内存
      return;
    }

    // 解析Wi-Fi配置
    const char *newSSID = doc["wifi"]["ssid"];      // 提取SSID
    const char *newPassword = doc["wifi"]["pass"];  // 提取密码
    const char *method = doc["method"];
    bool params = doc["params"];

    // 检查是否包含 Wi-Fi 配置信息
    if (newSSID && newPassword) {
      Serial.println("连接到新的Wi-Fi:");
      Serial.println("SSID: " + String(newSSID));
      Serial.println("Password: " + String(newPassword));

      // 尝试连接到新的Wi-Fi网络
      WiFi.begin(newSSID, newPassword);
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }

      Serial.println("\n连接到Wi-Fi!");
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());

      // Wi-Fi连接成功后，向手机发送蓝牙提示
      const char *successMessage = "{\"status\":\"ok\"}";
      pCharacteristic3->setValue((uint8_t *)successMessage, strlen(successMessage));
      pCharacteristic3->notify();  // 通过 BLE 通知手机 Wi-Fi 连接成功
    } else {
      // 检查是否包含 GPIO 控制命令

      if (method && strcmp(method, "setValue") == 0) {
        if (params) {
          RelayState = true;
          Serial.println("继电器 开启");
          // 发送执行结果给手机
          resultMessage = "{\"status\":\"Relay ON\"}";
        } else {
          RelayState = false;
          Serial.println("继电器 关闭");
          // 发送执行结果给手机
          resultMessage = "{\"status\":\"Relay OFF\"}";
        }
        digitalWrite(RelayPin, RelayState);
        pCharacteristic1->setValue((uint8_t *)resultMessage, strlen(resultMessage));
        pCharacteristic1->notify();  // 通过 BLE 通知手机操作已执行
      }  else if (method && strcmp(method, "setLedValue") == 0) {
        if (params) {
          LedState = true;
          Serial.println("LED 开启");
          // 发送执行结果给手机
          resultMessage = "{\"status\":\"Led ON\"}";
        } else {
          LedState = false;
          Serial.println("LED 关闭");
          // 发送执行结果给手机
          resultMessage = "{\"status\":\"Led OFF\"}";
        }
        digitalWrite(LedPin, !LedState);
        pCharacteristic1->setValue((uint8_t *)resultMessage, strlen(resultMessage));
        pCharacteristic1->notify();  // 通过 BLE 通知手机操作已执行
      }else {
        Serial.println("JSON 格式无效!");
      }
    }

    // 释放内存
    free(receivedData);
  }
  // void onwrite(uint16_t prf_id, uint16_t att_idx, uint8_t* data, uint16_t data_len) {
  //     // 打印出写入操作的 profile ID、attribute index 和数据长度
  //     Serial.printf("prf_id:%d, att_idx:%d, data_len:%d\r\n", prf_id, att_idx, data_len);

  //     // 使用 new 动态分配内存
  //     char* receivedData = new char[data_len + 1];

  //     // 将数据拷贝到字符串数组中
  //     memcpy(receivedData, data, data_len);
  //     receivedData[data_len] = '\0';  // 添加字符串结束符

  //     // 打印接收到的字符串
  //     Serial.printf("Received data: %s\r\n", receivedData);

  //     // 释放内存
  //     delete[] receivedData;
  // }

  // 当客户端从特征读取数据时触发
  void onread(uint16_t prf_id, uint16_t att_idx, uint8_t *data, uint16_t *data_len) {
    Serial.printf("prf_id:%d,att_idx:%d\r\n", prf_id, att_idx);  // 打印读取请求的信息
    // 向客户端返回固定的数据
    data[0] = 0xff;  // 设置返回的数据
    data[1] = 0xee;
    data[2] = 0xdd;
    *data_len = 3;  // 设置返回数据的长度,数据长度为3字节
  }
};

// 处理MQTT重连的函数
void reconnect() {
  int retryCount = 0;
  while (!client.connected() && retryCount < 5) {
    if (client.connect(clientId.c_str(), user, pass)) {
      client.subscribe(subTopic);  // 订阅主题
      Serial.println("MQTT 连接正常");
    } else {
      retryCount++;
      Serial.print("MQTT 连接失败, 5 秒后重试…");
      delay(5000);
    }
  }
  if (!client.connected()) {
    Serial.println("重试后MQTT连接失败,继续而没有连接。");
  }
}
void setup()
{
  Serial.begin(115200);
  pinMode(RelayPin, OUTPUT);
  pinMode(LedPin, OUTPUT);
  digitalWrite(RelayPin, RelayState);  // 设置GPIO 28的初始状态
  digitalWrite(LedPin, LedState);

  String macAddress = WiFi.macAddress(); // 获取 MAC 地址
  clientId = macAddress;                 // 使用 MAC 地址生成客户端 ID
  Serial.print("客户端 ID: ");
  Serial.println(clientId);
  macAddress.replace(":", ""); // 去掉 MAC 地址中的冒号
  // 生成带随机后缀的蓝牙名称
  String deviceName = "BK7238_WB3S_Relay_X1_" + String(random(1000, 9999));
  Serial.print("设备名称: ");
  Serial.println(deviceName);

  // 初始化 BLE 堆栈
  BLEDEV::init();

  // 创建一个 BLE 服务器
  BLEServer *pServer = BLEDEV::createServer();

  // 获取BLE广播对象
  pAdvertising = BLEDEV::getAdvertising();

  // 创建第一个服务
  BLEService *pService1 = pServer->createService(SERVICE_UUID1);

  // 创建第二个服务
  BLEService *pService2 = pServer->createService(SERVICE_UUID2);

  // 为服务器设置自定义的回调
  pServer->setCallbacks(new MyServerCallbacks());

  // 为第一个服务创建特征1，支持读、写、通知
  pCharacteristic1 = pService1->createCharacteristic(
    CHARACTERISTIC_UUID1,
    BLECharacteristic::PROPERTY_READ |     // 允许读取
      BLECharacteristic::PROPERTY_WRITE |  // 允许写入
      BLECharacteristic::PROPERTY_NOTIFY   // 允许通知
  );

  // 为第一个服务创建特征2，支持写和指示
  pCharacteristic2 = pService1->createCharacteristic(
    CHARACTERISTIC_UUID2,
    BLECharacteristic::PROPERTY_WRITE |     // 允许写入
      BLECharacteristic::PROPERTY_INDICATE  // 允许指示
  );

  // 为第二个服务创建特征3，支持读写
  pCharacteristic3 = pService2->createCharacteristic(
    CHARACTERISTIC_UUID3,
    BLECharacteristic::PROPERTY_WRITE |   // 允许写入
      BLECharacteristic::PROPERTY_READ |  // 允许读取
      BLECharacteristic::PROPERTY_NOTIFY  // 允许通知
  );
  // 启动两个服务
  pService1->start();
  pService2->start();

  // 广播数据和响应数据的缓冲区
  memset(adv_data, 0, sizeof(adv_data));                                  // 清空广播缓冲区
  adv_data[0] = deviceName.length() + 1;                                  // 广播数据长度
  adv_data[1] = 0x09;                                                     // 完整设备名称类型
  memcpy(&adv_data[2], deviceName.c_str(), deviceName.length());          // 设备名称数据
  pAdvertising->setAdvertisementData(adv_data, deviceName.length() + 2);  // 设置广播数据

  // 设置扫描响应数据，使用相同的设备名称
  memset(adv_rsp_data, 0, sizeof(adv_rsp_data));                             // 清空响应缓冲区
  adv_rsp_data[0] = deviceName.length() + 1;                                 // 扫描响应数据长度
  adv_rsp_data[1] = 0x08;                                                    // 短名称类型
  memcpy(&adv_rsp_data[2], deviceName.c_str(), deviceName.length());         // 设备名称数据
  pAdvertising->setScanResponseData(adv_rsp_data, deviceName.length() + 2);  // 设置扫描响应数据

  // 开始广播
  pAdvertising->start();

  // 设置热点名称为蓝牙设备名称
  apssid = deviceName.c_str(); // 将热点名称设置为蓝牙名称
  // 创建热点
  WiFi.softAP(apssid, appassword);
  Serial.println("热点名称: " + String(apssid));
  //  打印热点 IP
  Serial.print("AP 接入的 IP:");
  Serial.println(WiFi.softAPIP());

  // 检查是否有保存的Wi-Fi配置信息
  if (WiFi.status() != WL_CONNECTED)
  {
    WiFi.begin(); // 尝试自动连接上次保存的Wi-Fi
    Serial.println("尝试连接已保存的WiFi...");

    // 等待连接成功
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 20)
    {
      delay(500);
      Serial.print(".");
      attempts++;
    }
  }
  Serial.println("WIFI连接OK"); // Wi-Fi连接成功，输出提示信息
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  client.setServer(mqttServer, mqttPort); // 设置MQTT服务器的IP地址和端口号
  client.setCallback(mqttCallback);       // 设置MQTT消息回调函数
}

void loop() {
  connectToWiFi();
  if (!client.connected()) {
    reconnect();  // 如果MQTT客户端未连接，则重新连接
  }
  client.loop();  // 处理客户端的循环操作

  //   if (deviceConnected) { // 如果设备已连接
  //   delay(500);

  //   // 更新特征1的值并发送通知
  //   pCharacteristic1->setValue((uint8_t*)&value, 4);// 设置特征1的值
  //   pCharacteristic1->notify();// 通知特征1的值变化
  //   value++;  // 增加值
  //   delay(500);

  //   // 更新特征2的值并发送指示
  //   pCharacteristic2->setValue((uint8_t*)&value, 4);// 设置特征2的值
  //   pCharacteristic2->indicate();// 通知特征2的值变化

  //   delay(500);
  // }

  // 检查是否到了上传RSSI的时间
  unsigned long currentMillis = millis();
  if (currentMillis - lastRSSIPublishTime >= RSSI_INTERVAL) {
    lastRSSIPublishTime = currentMillis;

    // 检查WiFi是否连接
    if (WiFi.status() == WL_CONNECTED) {
      int rssi = WiFi.RSSI();  // 获取WiFi信号强度
      Serial.print("WiFi信号强度RSSI: ");
      Serial.println(rssi);

      // 创建JSON文档，存储RSSI数据
      StaticJsonDocument<50> rssiDoc;
      rssiDoc["rssi"] = rssi;

      // 序列化JSON并发布到Telemetry主题
      char telemetryData[50];
      serializeJson(rssiDoc, telemetryData);
      client.publish("v1/devices/me/telemetry", telemetryData);
      Serial.println("RSSI数据发布到ThingsBoard!");
    }
  }

  // 处理断开连接后的广告重启
  if (!deviceConnected && oldDeviceConnected) {  // 如果设备已断开而之前已连接
    delay(500);                                  // 给蓝牙堆栈一些时间准备
    pAdvertising->start();                       // 重新启动广播
    Serial.println("重新开始广播");               // 打印开始广播的信息
    oldDeviceConnected = deviceConnected;        // 更新旧连接状态
  }

  // 处理新连接的设备
  if (deviceConnected && !oldDeviceConnected) {  // 如果设备已连接而之前未连接
    oldDeviceConnected = deviceConnected;        // 更新旧连接状态
  }
}

// MQTT消息回调函数
void mqttCallback(char *topic, byte *payload, unsigned int length) {
  char *ret = strrchr(topic, '/');  // 从主题中查找最后一个“/”的位置
  if (ret == NULL)                  // 如果没有找到，则返回
    return;

  String pubTopicTemp = pubTopic + ret;  // 拼接新的发布主题

  // 创建一个JSON文档用于存储解析后的MQTT消息
  StaticJsonDocument<200> doc;
  DeserializationError error = deserializeJson(doc, payload, length);  // 反序列化JSON数据
  if (error) {                                                         // 如果反序列化失败
    Serial.print("反序列化 Json()失败: ");
    Serial.println(error.c_str());  // 输出错误信息
    return;
  }

  const char *method = doc["method"];  // 从JSON消息中提取 "method" 字段
  if (method == NULL) {                // 如果method字段为空则返回
    return;
  }

  // 根据 "method" 字段来设置或获取 GPIO 23 的状态
  if (strcmp(method, "setValue") == 0) {
    RelayState = doc["params"];  // 更新标志位，设置GPIO 28的状态
    Serial.println("将 GPIO RelayPin 设置为: " + String(RelayState));
    digitalWrite(RelayPin, RelayState ? HIGH : LOW);  // 根据标志位的值设置GPIO 28的状态
                                                      // 通过蓝牙发送 GPIO 状态
    if (RelayState) {
      Serial.println("继电器 开启");
      stateMessage = "{\"setRelayValue\":\"ture\"}";
    } else {
      Serial.println("继电器 关闭");
      stateMessage = "{\"setRelayValue\":\"false\"}";
    }
  } else if (strcmp(method, "getValue") == 0) {
    // 如果method为"getValue"，则返回当前GPIO 28的状态
    if (RelayState) {
      Serial.println("继电器状态:  开启");
      client.publish(pubTopicTemp.c_str(), "true");  // 通过MQTT发布状态为true
      stateMessage = "{\"getRelayValue\":\"ture\"}";
    } else {
      Serial.println("继电器状态:  关闭");
      client.publish(pubTopicTemp.c_str(), "false");  // 通过MQTT发布状态为false
      stateMessage = "{\"getRelayValue\":\"false\"}";
    }
  }else if (strcmp(method, "setLedValue") == 0) {
    LedState = doc["params"];  // 更新标志位，设置继电器的状态
    Serial.println("将 GPIO LedPin 设置为: " + String(!LedState));
    digitalWrite(LedPin, !LedState);  // 根据标志位的值设置GPIO 28的状态
                                                      // 通过蓝牙发送 GPIO 状态
    if (LedState) {
      Serial.println("LED 开启");
      stateMessage = "{\"setRelayValue\":\"ture\"}";
    } else {
      Serial.println("LED 关闭");
      stateMessage = "{\"setRelayValue\":\"false\"}";
    }
  }else if (strcmp(method, "getLedValue") == 0) {
    // 如果method为"getLedValue"，则返回当前LED状态
    if (LedState) {
      Serial.println("LED状态:  开启");
      client.publish(pubTopicTemp.c_str(), "true");  // 通过MQTT发布状态为true
      stateMessage = "{\"getLedValue\":\"ture\"}";
    } else {
      Serial.println("LED状态:  关闭");
      client.publish(pubTopicTemp.c_str(), "false");  // 通过MQTT发布状态为false
      stateMessage = "{\"getLedValue\":\"false\"}";
    }
  }
  pCharacteristic1->setValue((uint8_t *)stateMessage, strlen(stateMessage));
  pCharacteristic1->notify();  // 通过蓝牙通知设备
}
void connectToWiFi()  // WiFi重连函数
{
  if (WiFi.status() == WL_CONNECTED)
    return;

  Serial.print("连接到 Wi-Fi…");
  WiFi.begin();
  unsigned long startAttemptTime = millis();

  while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 10000) {
    delay(500);
    Serial.print('.');
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("连接完成!");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println("Wi-Fi 连接失败。在循环中重试。");
  }
}
